﻿using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using Random = UnityEngine.Random;

namespace Mars.GpuInstance
{
    public class TestGPUAnimator : MonoBehaviour
    {
        public Camera camera;
        public float speed = 1f;
        public int count = 1000;
        public float animatorSpeed = 1.0f;
        public int clipIndex = 0;

        public bool randomClip = false;
        public bool useCrossFade = false;
        public string anchorName = "";
        public bool showAnchor = false;
        public GPUAnimatorGenConfig prefab;

        public Animator actorAnimator;
        
        private List<int> instances = new List<int>();
        private List<GameObject> anchors = new List<GameObject>();
        
        public bool randomInput = false;
        public float randomInputInterval = 2;
        private float randomInputTimer = 0;

        private void Start()
        {
            if (camera == null)
            {
                camera = Camera.main;
            }

            var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();

            var cameraPos = camera.transform.position;
            for(var i = 0; i < count; i++)
            {
                var r = Quaternion.identity;
                var s = new Vector3(1,1,1);
                if (prefab)
                {
                    instancerProceduralManager.AddInstance(prefab,i, GetPosition(i, 0), s, r);

                    var idx = clipIndex;
                    if (randomClip)
                    {
                        var count = prefab.animationData.clipDataList.Count;
                        idx =  Random.Range(0, count);
                    }
                    instancerProceduralManager.PlayAnimatorByIndex(prefab, i, idx);
                    instancerProceduralManager.SetAnimationSpeed(prefab, i, animatorSpeed);
                    
                    instances.Add(i);
                }
            }
        }
        
        private Vector3 GetPosition(int index, float y)
        {
            int col = index % 100;
            int row = index / 100;

            return new Vector3(col * 5, y, row * 5);
        }
        
        private Vector3 GetRandomPos(Vector3 centerPos)
        {
            return new Vector3(
                centerPos.x + UnityEngine.Random.Range(-10, 10),
                centerPos.y + UnityEngine.Random.Range(-10, 10),
                centerPos.z + UnityEngine.Random.Range(-10, 10));
        }
        
        private GameObject CreateAnchor(int index, string name)
        {
            string anchorName = $"anchor_{name}_{index}";

            if (anchors.Count > index)
            {
                var go = anchors[index];
                go.name = anchorName;
                return anchors[index];
            }
            else
            {
                var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                go.transform.localScale = Vector3.one * 0.1f; 
                go.name = anchorName;
                anchors.Add(go);
                return go;
            }
        }

        public float4 curQuat;
        private void Update()
        {
            Shader.EnableKeyword("_UNITY_EDITOR_ONLY_PREVIEW_DEFAULT_LANDFORM_COLOR");
            curQuat = new float4(transform.rotation.x, transform.rotation.y, transform.rotation.z, transform.rotation.w);
            if (showAnchor && prefab != null)
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                var hash = Animator.StringToHash(anchorName);
                for (int i = 0; i < instances.Count; i++)
                {
                    var anchorInfo = instancerProceduralManager.GetAnchorInfo(prefab, instances[i], hash);
                    var go = CreateAnchor(i, anchorName);
                    go.transform.SetPositionAndRotation(anchorInfo.position, anchorInfo.rotation);
                }
            }
            
            if (camera)
            {
                if (Input.GetKey(KeyCode.A))
                {
                    camera.transform.position -= camera.transform.right * speed;
                }

                if (Input.GetKey(KeyCode.D))
                {
                    camera.transform.position += camera.transform.right * speed;
                }

                if (Input.GetKey(KeyCode.W))
                {
                    camera.transform.position += new Vector3(0, 0, 1) * speed;
                }

                if (Input.GetKey(KeyCode.S))
                {
                    camera.transform.position -= new Vector3(0, 0, 1) * speed;
                }
            }


            if (Input.GetKeyUp(KeyCode.V))
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                instancerProceduralManager.ClearInstance(prefab);
            }
            
            if (Input.GetKeyUp(KeyCode.J))
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                for(var i = 0; i < count; i++)
                {
                    if (prefab)
                    {
                        if (useCrossFade)
                        {
                            var clipName = prefab.animationData.clipDataList[0].clipName;
                            var animHash = Animator.StringToHash(clipName);
                            instancerProceduralManager.CrossFadeAnimator(prefab, i, animHash);
                        }
                        else
                        {
                            instancerProceduralManager.PlayAnimatorByIndex(prefab, i, 0);
                        }
                    }
                }
            }
            
            if (Input.GetKeyUp(KeyCode.K))
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                for(var i = 0; i < count; i++)
                {
                    if (useCrossFade)
                    {
                        var clipName = prefab.animationData.clipDataList[1].clipName;
                        var animHash = Animator.StringToHash(clipName);
                        instancerProceduralManager.CrossFadeAnimator(prefab, i, animHash);
                    }
                    else
                    {
                        instancerProceduralManager.PlayAnimatorByIndex(prefab, i, 1);
                    }
                }
            }
            
            if (Input.GetKeyUp(KeyCode.L))
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                for(var i = 0; i < count; i++)
                {
                    if (prefab)
                    {
                        instancerProceduralManager.PlayAnimatorByIndex(prefab, i, 7);
                    }
                }
            }
            
            if (Input.GetKeyUp(KeyCode.O))
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                for(var i = 0; i < count; i++)
                {
                    if (prefab)
                    {
                        var count = prefab.animationData.clipDataList.Count;
                        var clipIndex =  Random.Range(0, count);
                        instancerProceduralManager.PlayAnimatorByIndex(prefab, i, clipIndex);
                    }
                }
            }

            if (Input.GetKeyUp(KeyCode.Keypad0))
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                var animHash = Animator.StringToHash("guard");
                ActorPlay(animHash);
                for(var i = 0; i < count; i++)
                {
                    if (prefab)
                    {
                        if (useCrossFade)
                            instancerProceduralManager.CrossFadeAnimator(prefab, i, animHash);
                        else
                            instancerProceduralManager.PlayAnimator(prefab, i, animHash, 0, 1);
                    }
                }
            }
            
            if (Input.GetKeyUp(KeyCode.Keypad1))
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                var animHash = Animator.StringToHash("walk");
                ActorPlay(animHash);
                for(var i = 0; i < count; i++)
                {
                    if (prefab)
                    {
                        if (useCrossFade)
                            instancerProceduralManager.CrossFadeAnimator(prefab, i, animHash);
                        else
                            instancerProceduralManager.PlayAnimator(prefab, i, animHash, 0, 1);
                    }
                }
            }
            
            if (Input.GetKeyUp(KeyCode.Keypad2))
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                var animHash = Animator.StringToHash("run");
                ActorPlay(animHash);
                for(var i = 0; i < count; i++)
                {
                    if (prefab)
                    {
                        if (useCrossFade)
                            instancerProceduralManager.CrossFadeAnimator(prefab, i, animHash);
                        else
                            instancerProceduralManager.PlayAnimator(prefab, i, animHash, 0, 1);
                    }
                }
            }
            
            if (Input.GetKeyUp(KeyCode.Keypad3))
            {
                var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                var animHash = Animator.StringToHash("win");
                ActorPlay(animHash);
                for(var i = 0; i < count; i++)
                {
                    if (prefab)
                    {
                        if (useCrossFade)
                            instancerProceduralManager.CrossFadeAnimator(prefab, i, animHash);
                        else
                            instancerProceduralManager.PlayAnimator(prefab, i, animHash, 0, 1);
                    }
                }
            }

            void ActorPlay(int animHash)
            {
                if (actorAnimator)
                {
                    if (useCrossFade)
                        actorAnimator.CrossFade(animHash, 0.2f);
                    else
                        actorAnimator.Play(animHash);
                }
            }

            if (randomInput)
            {
                randomInputTimer += Time.deltaTime;
                if (randomInputTimer > randomInputInterval)
                {
                    randomInputTimer = 0;
                    var instancerProceduralManager = GPUAnimatorInstancerProceduralManager.GetInstance();
                    for(var i = 0; i < count; i++)
                    {
                        if (prefab)
                        {
                            var animIndex = Random.Range(0, prefab.animationData.clipDataList.Count - 1);
                            if (useCrossFade)
                            {
                                var clipName = prefab.animationData.clipDataList[animIndex].clipName;
                                var animHash = Animator.StringToHash(clipName);
                                instancerProceduralManager.CrossFadeAnimator(prefab, i, animHash);
                            }
                            else
                            {
                                instancerProceduralManager.PlayAnimatorByIndex(prefab, i, animIndex);
                            }
                        }
                    }
                }
            }
        }
        
        public void DecomposeMatrix()
        {
            TryDecomposeMatrix(transform.localToWorldMatrix, out var position, out var rotation, out var scale);
        }
        
        public void TryDecomposeMatrix(Matrix4x4 matrix, out Vector3 position, out Quaternion rotation, out Vector3 scale)
        {
            position = matrix.GetColumn(3);
            rotation = Quaternion.LookRotation(matrix.GetColumn(2), matrix.GetColumn(1));
            scale = new Vector3(matrix.GetColumn(0).magnitude, matrix.GetColumn(1).magnitude, matrix.GetColumn(2).magnitude);
            
            Debug.Log($"[TestGPUAnimator] try decompose matrix. position: {position}, rotation: {rotation.eulerAngles}, scale: {scale}");
        }
    }
}
